﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using SecuritiesAnalysis.Common;
using SecuritiesAnalysis.Interface;
using SecuritiesAnalysis.CurrentData;

namespace SecuritiesAnalysis
{
    public class MACDCalculator : ICalculator
    {
        private SecuritiesAnalysisDataContext ctx;
        private List<T_HistoryPrice> historyList;
        private List<T_Info_MACD> MACDList;
        private List<T_StockCode> CodeList;
        private DateTime latest;
        private T_Info_MACD macdmod;

        public MACDCalculator()
        {
            this.ctx = new SecuritiesAnalysisDataContext();
        }

        #region ICalculator Members for ALL operation, copy from MACalculator

        public bool Clear()
        {
            ctx.ExecuteCommand("TRUNCATE TABLE T_Info_MACD");
            return true;
        }

        public bool ReCalculate()
        {
            Clear();

            CodeList = ctx.T_StockCodes.Where(c => c.Enabled == true).ToList();

            foreach (T_StockCode code in CodeList)
            {
                if (!ReCalculate(code.StockCode))
                {
                    return false;
                }
            }

            return true;
        }

        public bool Calculate()
        {
            CodeList = ctx.T_StockCodes.Where(c => c.Enabled == true).ToList();

            foreach (T_StockCode code in CodeList)
            {
                if (!Calculate(code.StockCode))
                {
                    return false;
                }
            }

            return true;
        }

        public bool CalculateToday()
        {
            CodeList = ctx.T_StockCodes.Where(c => c.Enabled == true).ToList();

            #region 判断是否数据库中有最新记录，如果没有则重新添加，希望能够提高效率
            latest = GetLatestHistoryDate(CodeList[1].StockCode);

            if (latest < DateTime.Today)
            {
                //获取并插入最新记录
                using (DataSpider ds = new DataSpider())
                {
                    ds.UpdateCurrentPrice();
                }
            }
            #endregion

            foreach (T_StockCode code in CodeList)
            {
                if (!CalculateToday(code.StockCode))
                {
                    return false;
                }
            }

            return true;
        }

        #endregion

        #region ICalculator Members

        public bool Clear(int stockcode)
        {
            try
            {
                ctx.ExecuteCommand("DELETE FROM T_Info_MACD WHERE [STOCKCODE]=" + stockcode);

                return true;
            }
            catch (Exception e)
            {
                Log.OutputError(e.Message);
                return false;
            }
        }

        public bool ReCalculate(int stockcode)
        {
            Clear(stockcode);

            try
            {
                historyList = ctx.T_HistoryPrices.Where(c => c.StockCode == stockcode).OrderBy(c => c.Date).ToList();//按日期升序
                MACDList = new List<T_Info_MACD>();

                for (int i = 0; i < historyList.Count; i++)
                {
                    T_Info_MACD mod = new T_Info_MACD();
                    mod.StockCode = stockcode;
                    mod.Date = historyList[i].Date;

                    //mod.MA3 = GetMA(3, i);
                    //mod.MA5 = GetMA(5, i);
                    //mod.MA21 = GetMA(21, i);

                    MACDList.Add(mod);
                }

                ctx.T_Info_MACDs.InsertAllOnSubmit(MACDList);
                ctx.SubmitChanges();

                return true;
            }
            catch (Exception e)
            {
                Log.OutputError(e.Message);
                return false;
            }
        }


        public bool Calculate(int stockcode)
        {
            try
            {
                latest = GetLatestDate(stockcode);

                historyList = ctx.T_HistoryPrices.Where(c => c.StockCode == stockcode).OrderBy(c => c.Date).ToList();//按日期升序
                MACDList = new List<T_Info_MACD>();

                for (int i = 0; i < historyList.Count; i++)
                {
                    if (historyList[i].Date <= latest)
                    {
                        //过滤已经计算过的日期
                        continue;
                    }

                    T_Info_MACD mod = new T_Info_MACD();
                    mod.StockCode = stockcode;
                    mod.Date = historyList[i].Date;

                    //mod.MA3 = GetMA(3, i);
                    //mod.MA5 = GetMA(5, i);
                    //mod.MA21 = GetMA(21, i);

                    MACDList.Add(mod);
                }

                ctx.T_Info_MACDs.InsertAllOnSubmit(MACDList);
                ctx.SubmitChanges();

                return true;
            }
            catch (Exception e)
            {
                Log.OutputError(e.Message);
                return false;
            }
        }

        public bool CalculateToday(int stockcode)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region private methods

        /// <summary>
        /// 获取当前某股票最新历史数据
        /// </summary>
        /// <param name="stockcode">股票代码</param>
        /// <returns>最新历史数据</returns>
        private DateTime GetLatestHistoryDate(int stockcode)
        {
            try
            {
                T_HistoryPrice latest = ctx.T_HistoryPrices.Where(o => o.StockCode == stockcode).OrderByDescending(o => o.Date).First();
                return latest.Date;
            }
            catch (Exception e)
            {
                if (e.Message == "Sequence contains no elements")
                {
                    return DateTime.MinValue;
                }
                return DateTime.MinValue;
            }
        }

        /// <summary>
        /// 获取当前某股票已经计算的最新MA日期
        /// </summary>
        /// <param name="stockcode">股票代码</param>
        /// <returns>最新日期</returns>
        private DateTime GetLatestDate(int stockcode)
        {
            try
            {
                T_Info_MACD latest = ctx.T_Info_MACDs.Where(o => o.StockCode == stockcode).OrderByDescending(o => o.Date).First();
                return latest.Date;
            }
            catch (Exception e)
            {
                if (e.Message == "Sequence contains no elements")
                {
                    return DateTime.MinValue;
                }
                return DateTime.MinValue;
            }
        }

        ///// <summary>
        ///// 计算列表的DI值
        ///// </summary>
        ///// <param name="list">要计算的模型列表</param>
        ///// <param name="needsave">是否保存到数据库</param>
        ///// <param name="hisList">相关的历史数据列表</param>
        ///// <returns>是否执行成功</returns>
        //private bool CalculateDIs(List<T_Info_MACD> list, bool needsave, List<T_HistoryPrice> hisList)
        //{
        //    foreach (T_Info_MACD mod in list)
        //    {
        //        mod.DI=
        //    }

        //        return true;
        //}

        /*
         我按下面这些做怎么也算不出正确的数据。

            MACD是计算两条不同速度（长期与中期）的指数平滑移动平均线（EMA）的差离状况来作为研判行情的基础。 
            1 先求出每天的DI值 DI=H＋L+ 2C 
            2、第一天计算时 AX=（12天DI之和）/12 
                                  BX＝（26天DI之和）/26 
            3、将第13天之 DI减去 AX再乘以0.1538再加上AN得出DIAX值。 
            4、将第26天之DI减去BX再乘以0.0741再加上BX得出DIBX。
            5、从第27天起将每天的 DIAX－DIBX,即得出DIF值。 
            6、第一次计算MACD时,则以9天的DIF总和/9,所得出之值代替第一个MACD。 
            7、由第36天的 DIF减去第35大的MACD所得之值乘以0.2再加上第35天的 MACD,即可算出第36天的MACD。 
               别忘了，第35天的MACD是由第6式之值代替的。 
               第37天以后，每天只要将当天的DIF减去昨天的 MACD乘以 0.2以后再加上昨日的MACD即可
            转一个简单版的...希望楼主能看明白
            MACD在应用上，应先行计算出快速移动平均线 (12日EMA)与慢速移动平均线(26日EMA)，并以这两个数值，
            作为测量两者(快速与慢速线)间的“差离值”依据。所谓“差离值”(DIF)即 12日EMA的数值减去26日EMA的数值。
　　            系统和系统的默认值均设定MACD快速EMA参数为12，MACD慢速EMA参数为26，DIF参数为9，则：
　　            1、某一天DI=H(最高价)+L(最低价)+2C(收盘价)；
　　            2、AX=12天DI之和÷12；
　　            3、BX=26天DI之和÷26；
　　            4、DIAX=(第13天的DI-AX)×0.1538+AX；
　　            5、DIBX=(第26天的DI-BX)×0.0741+BX；
　　            6、自第27天起，将每天的DIAX减去DIBX，即得出DIF值；
　　            7、第一次计算MACD时，则以9天的DIF总和÷9，得出之值作为第一个MACD；
　　            8、第十天以后：第n天的MACD＝（第n天的DIF－第（n-1）天的MACD）×0.2+第（n－1）天的MACD。比如说
            ： MACD(第36天)＝（第36天的DIF－第35天的MACD）×0.2+第35天的MACD。也就是：用第36天的DIF减去第35天的
            MACD(即步骤7算出的第一个MACD)，所得之值乘以0.2，再加上第35天的MACD，即可求出第36天的MACD；
　　            以此类推，可得出一条MACD线。
                为了更进一步的说明问题，我们给出一个例子，华邦制药（002004）上市开始的数据：
              DI数据如下：
              1  22.88+18.80+19.27*2＝80.22
              2  18.00+17.34+17.34*2＝66.42
              3  17.00+15.62+16.08*2＝64.78
              4  16.97+16.31+16.40*2＝66.08
              5  16.84+16.30+16.50*2＝66.14
              6  16.75+15.22+15.65*2＝63.27
              7  15.80+15.30+15.62*2＝62.34
              8  16.20+15.52+15.85*2＝63.42
              9  15.85+15.25+15.35*2＝61.8
              10 15.51+15.05+15.30*2＝61.16
              11 15.79+15.26+15.31*2＝61.67
              12 15.30+14.00+14.02*2＝57.34
              13 14.20+13.31+13.76*2＝55.03
         */

        private List<T_Info_MACD> GetMACD()
        {
            List<T_Info_MACD> list = new List<T_Info_MACD>();

            foreach (T_HistoryPrice hmod in historyList)
            {
                T_Info_MACD mod = new T_Info_MACD();
                mod.Date = hmod.Date;
                mod.StockCode = hmod.StockCode;
                mod.DI = hmod.CeilingPrice + hmod.FloorPrice + hmod.ClosingPrice * 2;//DI=H+L+2C

                list.Add(mod);
            }

            

            return null;
        }

        #endregion
    }
}
